home *** CD-ROM | disk | FTP | other *** search
/ Aminet 1 (Walnut Creek) / Aminet - June 1993 [Walnut Creek].iso / aminet / util / gnu / fileutils_3_3.lha / fileutils-3.3 / lib / makepath.c < prev    next >
C/C++ Source or Header  |  1992-08-02  |  6KB  |  259 lines

  1. /* makepath.c -- Ensure that a directory path exists.
  2.    Copyright (C) 1990 Free Software Foundation, Inc.
  3.  
  4.    This program is free software; you can redistribute it and/or modify
  5.    it under the terms of the GNU General Public License as published by
  6.    the Free Software Foundation; either version 2, or (at your option)
  7.    any later version.
  8.  
  9.    This program is distributed in the hope that it will be useful,
  10.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.    GNU General Public License for more details.
  13.  
  14.    You should have received a copy of the GNU General Public License
  15.    along with this program; if not, write to the Free Software
  16.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  17.  
  18. /* Written by David MacKenzie <djm@ai.mit.edu> and
  19.    Jim Meyering <meyering@cs.utexas.edu>.  */
  20.  
  21. #ifdef __GNUC__
  22. #define alloca __builtin_alloca
  23. #else
  24. #ifdef sparc
  25. #include <alloca.h>
  26. #else
  27. #ifdef _AIX
  28.  #pragma alloca
  29. #else
  30. char *alloca ();
  31. #endif
  32. #endif
  33. #endif
  34.  
  35. #include <stdio.h>
  36. #include <sys/types.h>
  37. #include <sys/stat.h>
  38. #ifdef HAVE_UNISTD_H
  39. #include <unistd.h>
  40. #endif
  41. #if !defined(S_ISDIR) && defined(S_IFDIR)
  42. #define    S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
  43. #endif
  44.  
  45. #ifdef STDC_HEADERS
  46. #include <errno.h>
  47. #include <stdlib.h>
  48. #else
  49. extern int errno;
  50. #endif
  51.  
  52. #if defined(USG) || defined(STDC_HEADERS)
  53. #include <string.h>
  54. #define index strchr
  55. #else
  56. #include <strings.h>
  57. #endif
  58.  
  59. void error ();
  60.  
  61. /* Ensure that the directory ARGPATH exists.
  62.    Remove any trailing slashes from ARGPATH before calling this function.
  63.  
  64.    Make any leading directories that don't already exist, with
  65.    permissions PARENT_MODE.
  66.    If the last element of ARGPATH does not exist, create it as
  67.    a new directory with permissions MODE.
  68.    If OWNER and GROUP are non-negative, make them the UID and GID of
  69.    created directories.
  70.    If VERBOSE_FMT_STRING is nonzero, use it as a printf format
  71.    string for printing a message after successfully making a directory,
  72.    with the name of the directory that was just made as an argument.
  73.  
  74.    Return 0 if ARGPATH exists as a directory with the proper
  75.    ownership and permissions when done, otherwise 1.  */
  76.  
  77. int
  78. make_path (argpath, mode, parent_mode, owner, group, verbose_fmt_string)
  79.      char *argpath;
  80.      int mode;
  81.      int parent_mode;
  82.      int owner;
  83.      int group;
  84.      char *verbose_fmt_string;
  85. {
  86.   char *dirpath;        /* A copy we can scribble NULs on.  */
  87.   struct stat stats;
  88.   int retval = 0;
  89.   int oldmask = umask (0);
  90.  
  91.   dirpath = alloca (strlen (argpath) + 1);
  92.   strcpy (dirpath, argpath);
  93.  
  94.   if (stat (dirpath, &stats))
  95.     {
  96.       char *slash;
  97.       int tmp_mode;        /* Initial perms for leading dirs.  */
  98.       int re_protect;        /* Should leading dirs be unwritable? */
  99.       struct ptr_list
  100.       {
  101.     char *dirname_end;
  102.     struct ptr_list *next;
  103.       };
  104.       struct ptr_list *p, *leading_dirs = NULL;
  105.  
  106.       /* If leading directories shouldn't be writable or executable,
  107.      or should have set[ug]id or sticky bits set and we are setting
  108.      their owners, we need to fix their permissions after making them.  */
  109.       if (((parent_mode & 0300) != 0300)
  110.       || (owner >= 0 && group >= 0 && (parent_mode & 07000) != 0))
  111.     {
  112.       tmp_mode = 0700;
  113.       re_protect = 1;
  114.     }
  115.       else
  116.     {
  117.       tmp_mode = parent_mode;
  118.       re_protect = 0;
  119.     }
  120.  
  121.       slash = dirpath;
  122. #ifndef AMIGA
  123.       while (*slash == '/')
  124.     slash++;
  125. #endif
  126.       while (slash = index (slash, '/'))
  127.     {
  128.       *slash = '\0';
  129.       if (stat (dirpath, &stats))
  130.         {
  131.           if (mkdir (dirpath, tmp_mode))
  132.         {
  133.           error (0, errno, "cannot make directory `%s'", dirpath);
  134.           umask (oldmask);
  135.           return 1;
  136.         }
  137.           else
  138.         {
  139.           if (verbose_fmt_string != NULL)
  140.             error (0, 0, verbose_fmt_string, dirpath);
  141.  
  142.           if (owner >= 0 && group >= 0
  143.               && chown (dirpath, owner, group)
  144. #ifdef AFS
  145.               && errno != EPERM
  146. #endif
  147.               )
  148.             {
  149.               error (0, errno, "%s", dirpath);
  150.               retval = 1;
  151.             }
  152.           if (re_protect)
  153.             {
  154.               struct ptr_list *new = (struct ptr_list *)
  155.             alloca (sizeof (struct ptr_list));
  156.               new->dirname_end = slash;
  157.               new->next = leading_dirs;
  158.               leading_dirs = new;
  159.             }
  160.         }
  161.         }
  162.       else if (!S_ISDIR (stats.st_mode))
  163.         {
  164.           error (0, 0, "`%s' exists but is not a directory", dirpath);
  165.           umask (oldmask);
  166.           return 1;
  167.         }
  168.  
  169.       *slash++ = '/';
  170.  
  171. #ifndef AMIGA
  172.       /* Avoid unnecessary calls to `stat' when given
  173.          pathnames containing multiple adjacent slashes.  */
  174.       while (*slash == '/')
  175.         slash++;
  176. #endif
  177.     }
  178.  
  179.       /* We're done making leading directories.
  180.      Make the final component of the path.  */
  181.  
  182.       if (mkdir (dirpath, mode))
  183.     {
  184.       error (0, errno, "cannot make directory `%s'", dirpath);
  185.       umask (oldmask);
  186.       return 1;
  187.     }
  188.       if (verbose_fmt_string != NULL)
  189.     error (0, 0, verbose_fmt_string, dirpath);
  190.  
  191.       if (owner >= 0 && group >= 0)
  192.     {
  193.       if (chown (dirpath, owner, group)
  194. #ifdef AFS
  195.           && errno != EPERM
  196. #endif
  197.           )
  198.         {
  199.           error (0, errno, "%s", dirpath);
  200.           retval = 1;
  201.         }
  202.       /* chown may have turned off some permission bits we wanted.  */
  203.       if ((mode & 07000) != 0 && chmod (dirpath, mode))
  204.         {
  205.           error (0, errno, "%s", dirpath);
  206.           retval = 1;
  207.         }
  208.     }
  209.  
  210.       /* If the mode for leading directories didn't include owner "wx"
  211.      privileges, we have to reset their protections to the correct
  212.      value.  */
  213.       for (p = leading_dirs; p != NULL; p = p->next)
  214.     {
  215.       *(p->dirname_end) = '\0';
  216.       if (chmod (dirpath, parent_mode))
  217.         {
  218.           error (0, errno, "%s", dirpath);
  219.           retval = 1;
  220.         }
  221.     }
  222.     }
  223.   else
  224.     {
  225.       /* We get here if the entire path already exists.  */
  226.  
  227.       if (!S_ISDIR (stats.st_mode))
  228.     {
  229.       error (0, 0, "`%s' exists but is not a directory", dirpath);
  230.       umask (oldmask);
  231.       return 1;
  232.     }
  233.  
  234.       /* chown must precede chmod because on some systems,
  235.      chown clears the set[ug]id bits for non-superusers,
  236.      resulting in incorrect permissions.
  237.      On System V, users can give away files with chown and then not
  238.      be able to chmod them.  So don't give files away.  */
  239.  
  240.       if (owner >= 0 && group >= 0 && chown (dirpath, owner, group)
  241. #ifdef AFS
  242.       && errno != EPERM
  243. #endif
  244.       )
  245.     {
  246.       error (0, errno, "%s", dirpath);
  247.       retval = 1;
  248.     }
  249.       if (chmod (dirpath, mode))
  250.     {
  251.       error (0, errno, "%s", dirpath);
  252.       retval = 1;
  253.     }
  254.     }
  255.  
  256.   umask (oldmask);
  257.   return retval;
  258. }
  259.